package flare.query
{
  /**
   * Expression operator that performs function invocation. The set of
   * available functions is determined by a static function table maintained
   * by this class. The function table can be extended to introduce custom
   * functions into the query language.
   */
  public class Fn extends CompositeExpression
  {
    private var _name:String;
    private var _func:Function;
    private var _args:Array;

    /**
     * Creates a new Fn (function) operator.
     * @param name the name of the function. This should map to an entry
     *  in the static function table.
     * @params args sub-expressions for the function arguments
     */
    public function Fn(name:String = null, ...args)
    {
      if(name)this.name = name;
      super(args);
    }

    /**
     * Sets the name of the function. This should map to an entry
     *  in the static function table.
     */
    public function set name(n:String):void
    {
      _name = n.toUpperCase();
      _func = table[_name];
    }

    /**
     * @inheritDoc
     */
    public override function clone():Expression
    {
      return new Fn(_name, _children);
    }

    /**
     * @inheritDoc
     */
    public override function eval(o:Object = null):*
    {
      // first, initialize the argument array as needed
      if(_args == null || _args.length != _children.length)
      {
        _args = new Array(_children.length);
      }

      // now evaluate all sub-expressions
      for(var i:int = 0; i < _children.length; ++i)
      {
        _args[i] = _children[i].eval(o);
      }
      // now evaluate the function
      return _func.apply(null, _args);
    }

    /**
     * @inheritDoc
     */
    public override function toString():String
    {
      return _name + getString();
    }

    // --------------------------------------------------------------------

    /** 
     * Function table mapping function names to Function instances.
     * Functions can be added or removed at run-time by editing this
     * object.
     */
    public static var table:Object =
    {
      // Math Functions
      ABS: Math.abs,
      ACOS: Math.acos,
      ASIN: Math.asin,
      ATAN: Math.atan,
      ATAN2: Math.atan2,
      CEIL: Math.ceil,
      CEILING: Math.ceil,
      COS: Math.cos,
      EXP: Math.exp,
      FLOOR: Math.floor,
      LOG: Math.log,
      MAX: Math.max,
      MIN: Math.min,
      POW: Math.pow,
      POWER: Math.pow,
      RANDOM: Math.random,
      ROUND: Math.round,
      SIN: Math.sin,
      SQRT: Math.sqrt,
      TAN: Math.tan,

      // String Functions
      CONCAT: StringUtil.concat,
      CONCAT_WS: StringUtil.concat_ws,
      FORMAT: StringUtil.format,
      INSERT: StringUtil.insert,
      LEFT: StringUtil.left,
      LENGTH: StringUtil.length,
      LOWER: StringUtil.lower,
      LCASE: StringUtil.lower,
      LPAD: StringUtil.lpad,
      MID: StringUtil.substring,
      POSITION: StringUtil.position,
      REVERSE: StringUtil.reverse,
      REPEAT: StringUtil.repeat,
      REPLACE: StringUtil.replace,
      RIGHT: StringUtil.right,
      RPAD: StringUtil.rpad,
      SPACE: StringUtil.space,
      SUBSTRING: StringUtil.substring,
      UPPER: StringUtil.upper,
      UCASE: StringUtil.upper,

      // Date/Time Functions
      DATE: DateUtil.date,
      DAY: DateUtil.day,
      DAYOFWEEK: DateUtil.dayOfWeek,
      HOUR: DateUtil.hour,
      MICROSECOND: DateUtil.microsecond,
      MINUTE: DateUtil.minute,
      MONTH: DateUtil.month,
      NOW: DateUtil.now,
      SECOND: DateUtil.second,
      YEAR: DateUtil.year
    };
  } // end of class Fn
}